home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / util / cli / grep.lha / grep.c next >
Encoding:
C/C++ Source or Header  |  1992-06-13  |  9.8 KB  |  418 lines

  1. /*------------------------------------------------*
  2.  | File: grep.c - First release MLO 920611        |
  3.  | Last revised: v1.1 MLO 920612                  |
  4.  | Unix-like grep utility, for rev. 37 or greater |
  5.  | (OS 2.04) of the Amiga DOS. See Syntax() for a |
  6.  | short description of this program.             |
  7.  *------------------------------------------------*/
  8.  
  9. /**
  10.  | #include files
  11. **/
  12.  
  13. #include <stdio.h>                      /* Standard library */
  14. #include <stddef.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <exec/types.h>                 /* AmigaDOS includes */
  18. #include <exec/memory.h>
  19. #include <dos/dos.h>
  20. #include <dos/dosasl.h>
  21. #include <dos/dosextens.h>
  22. #include <clib/exec_protos.h>
  23. #include <clib/dos_protos.h>
  24.  
  25. /**
  26.  | #define's and local stuff
  27. **/
  28.  
  29. #define REVISION        "1.1"           /* Program version */
  30. #define LAST_CHANGED    "920612"
  31.  
  32. typedef short int       Boolean;        /* Boolean variables */
  33.  
  34. #define True            1
  35. #define False           0
  36.  
  37. #define MATCH_STATUS    0               /* Exit status */
  38. #define NOMATCH_STATUS  5
  39. #define ERROR_STATUS    10
  40.  
  41. #define OS2_REVISION    37              /* AmigaDOS min. release (2.04) */
  42.  
  43. #define FILENAME_MAX    128             /* Max. length of file names */
  44. #define LINE_LENGTH     256             /* Max. length of an input line */
  45.  
  46. /**
  47.  | Version Tag string
  48. **/
  49.  
  50. char VersionTag[] = "\0$VER: grep v" REVISION " - MLO " LAST_CHANGED;
  51.  
  52. /**
  53.  | Global variables: flags for various program options, exit status
  54. **/
  55.  
  56. Boolean CountOnly       = False;
  57. Boolean IgnoreCase      = False;
  58. Boolean PrintAll        = False;
  59. Boolean PrintName       = False;
  60. Boolean LineNumber      = False;
  61. Boolean Reverse         = False;
  62. Boolean StarEnabled     = False;
  63. Boolean BreakFlag       = False;
  64. int Status              = MATCH_STATUS;
  65.  
  66. /**
  67.  | Global varables: program buffers, pointers to AmigaDOS structures
  68. **/
  69.  
  70. extern struct DosLibrary *DOSBase;      /* Opened from c.o */
  71.  
  72. char *Pattern               = NULL;
  73. char *Line                  = NULL;
  74. struct AnchorPath *pAP      = NULL;
  75. size_t APlength             = sizeof(struct AnchorPath) + FILENAME_MAX;
  76.  
  77. /**
  78.  | ANSI procedure prototypes
  79. **/
  80.  
  81. Boolean CheckBreak(void);
  82. void    Cleanup(void);
  83. int     CXBRK(void);
  84. void    DoTheStuff(FILE *fp, char *name);
  85. void    Error(void);
  86. void    Init(void);
  87. char   *NextFile(char *pattern);
  88. void    NoMem(void);
  89. void    Syntax(void);
  90.  
  91. main(
  92.   int argc,
  93.   char **argv
  94. ){
  95.  
  96. /**
  97.  | Main program: general initialisation, then loop on program options.
  98. **/
  99.  
  100.   Init();
  101.  
  102.   while (--argc) {
  103.     if ( ((*++argv)[0] == '-') ) {
  104.       int i=1;
  105.       char c;
  106.  
  107.       while (c = (*argv)[i++]) {
  108.         switch (c) {
  109.           case 'c':
  110.           case 'C':
  111.             CountOnly = True;
  112.             break;
  113.           case 'f':
  114.           case 'F':
  115.             PrintAll = True;
  116.             break;
  117.           case 'i':
  118.           case 'I':
  119.             IgnoreCase = True;
  120.             break;
  121.           case 'l':
  122.           case 'L':
  123.             PrintName = True;
  124.             break;
  125.           case 'n':
  126.           case 'N':
  127.             LineNumber = True;
  128.             break;
  129.           case 'v':
  130.           case 'V':
  131.             Reverse = True;
  132.             break;
  133.           default:
  134.             Syntax();
  135.             Error();
  136.         }
  137.       }
  138.     } else if ((*argv)[0] == '?') {
  139.       Syntax();
  140.       Cleanup();
  141.     } else {
  142.       break;
  143.     }
  144.   }
  145.  
  146. /**
  147.  | First (required) argument: pattern to search for.
  148.  | As the pattern can be everywhere in the input line,
  149.  | put a '*' before and after before converting to the
  150.  | internal system format.
  151. **/
  152.  
  153.   if (argc--) {
  154.     size_t i = strlen(*argv);
  155.     size_t j = i + 3;
  156.     char *temp;
  157.     char star[] = "*";
  158.  
  159.     if ((temp = malloc(j)) == NULL)   NoMem();
  160.     else {
  161.       if ((Pattern = malloc(j *= 3)) == NULL) {
  162.         free(temp);
  163.         NoMem();
  164.       }
  165.     }
  166.  
  167.     *temp = *star;
  168.     strcpy(temp+1, *argv);
  169.     strcpy(temp+1+i, star);
  170.  
  171.     if (IgnoreCase)   (void) ParsePatternNoCase(temp, Pattern, j);
  172.     else              (void) ParsePattern(temp, Pattern, j);
  173.  
  174.     free(temp);
  175.     Status = NOMATCH_STATUS;
  176.  
  177.   } else {
  178.     Syntax();
  179.     Error();
  180.   }
  181.  
  182. /**
  183.  | Following (optional) arguments: file patterns.
  184.  | They will be decoded, and every file in turn scanned for the
  185.  | given pattern; if no argument is given, scan stdin.
  186.  | If a break (CTRL-C) is detected from DoTheStuff() (and then
  187.  | BreakFlag is non-zero) we must re-enter NextFile to release
  188.  | the pattern matching internal stuff (calling MatchEnd()).
  189. **/
  190.  
  191.   if (argc) {
  192.     while (!BreakFlag   &&   argc--) {
  193.       char *fil_nam;
  194.  
  195.       ++argv;
  196.       while ((fil_nam = NextFile(*argv)) != NULL) {
  197.         FILE *fp;
  198.  
  199.         if ((fp = fopen(fil_nam, "r")) == NULL) {
  200.           printf("Couldn't open file \"%s\" ...\n", fil_nam);
  201.           Status = ERROR_STATUS;
  202.         } else {
  203.           DoTheStuff(fp, fil_nam);
  204.           fclose(fp);
  205.         }
  206.       }
  207.     }
  208.   } else {
  209.     DoTheStuff(stdin, NULL);
  210.   }
  211.  
  212.   Cleanup();  
  213. }
  214.  
  215. Boolean CheckBreak(void)
  216. {
  217.  
  218. /**
  219.  | Internal CTRL-C detection routine (the signal is cleared
  220.  | from the call). An internal attention flag i set up, and
  221.  | non-zero is returned as function value.
  222. **/
  223.  
  224.   if (!BreakFlag   &&
  225.       ((SetSignal(0, SIGBREAKF_CTRL_C)) & SIGBREAKF_CTRL_C))
  226.           BreakFlag = True;
  227.  
  228.   return BreakFlag;
  229. }
  230.  
  231. void Cleanup(void)
  232. {
  233.  
  234. /**
  235.  | Releases all system resources, then terminates the program
  236.  | returning 'Status' to the operating system.
  237. **/
  238.  
  239.   if (pAP     != NULL)    FreeMem(pAP, APlength);
  240.   if (Line    != NULL)    free(Line);
  241.   if (Pattern != NULL)    free(Pattern);
  242.   if (StarEnabled)        DOSBase->dl_Root->rn_Flags &= ~RNF_WILDSTAR;
  243.  
  244.   exit(Status);
  245. }
  246.  
  247. int CXBRK(void)
  248. {
  249.  
  250. /**
  251.  | Supersedes SAS-C internal CTRL-C handling routine,
  252.  | that detects CTRL-C on I/O etc.
  253.  | That one sets the internal attention flag, then waits
  254.  | (until CheckBreak() will be called).
  255. **/
  256.  
  257.   BreakFlag = True;
  258.   return 0;
  259. }
  260.  
  261. void DoTheStuff(
  262.   FILE *fp,
  263.   char *name
  264. ){
  265.  
  266. /**
  267.  | Central control routine.
  268.  | Scans the file looking for the given pattern, and producing the
  269.  | required output.
  270. **/
  271.  
  272.   long lineCount  = 0;                /* Number of read lines */
  273.   long how_many   = 0;                /* Number of matching lines */
  274.   Boolean match;                      /* 'File name printed' flag */
  275.   Boolean file_out = False;
  276.  
  277.   if (PrintAll) {
  278.     file_out = True;
  279.     printf("Scanning file \"%s\" ...\n", name);
  280.   }
  281.  
  282.   while (!CheckBreak()   &&   fgets(Line, LINE_LENGTH, fp) != NULL) {
  283.     lineCount++;
  284.     match = IgnoreCase ? MatchPatternNoCase(Pattern, Line) :
  285.                          MatchPattern(Pattern, Line);
  286.  
  287.     if (match) {
  288.       if (Status == NOMATCH_STATUS)   Status = MATCH_STATUS;
  289.       if (!Reverse) {
  290.  
  291. Gotcha:
  292.         if (CountOnly) {
  293.           how_many++;
  294.         } else {
  295.           if (PrintName   &&   !file_out) {
  296.             file_out = True;
  297.             if (name != NULL)   printf("In file \"%s\" ...\n", name);
  298.           }
  299.           if (LineNumber)       printf("%ld: ", lineCount);
  300.           fputs(Line, stdout);
  301.         }
  302.       }
  303.     } else {
  304.       if (Reverse) goto Gotcha;
  305.     }
  306.   }
  307.  
  308.   if (!BreakFlag   &&   CountOnly   &&   how_many) {
  309.     if (PrintName   &&   name != NULL)  printf("In file \"%s\": ", name);
  310.     printf("%ld matches.\n", how_many);
  311.   }
  312.  
  313.   if (how_many = ferror(fp)) {
  314.     printf("DOS error %ld while reading file \"%s\" ...\n",
  315.             how_many, name);
  316.     Status = ERROR_STATUS;
  317.   }
  318. }
  319.  
  320. void Error(void)
  321. {
  322.   Status = ERROR_STATUS;
  323.   Cleanup();
  324. }
  325.  
  326. void Init(void)
  327. {
  328.  
  329. /**
  330.  | Initialisation routine.
  331.  | Checks dos.library version number; enables (if not already enabled)
  332.  | the use of the '*' as wildcard character; and obtains memory for
  333.  | the buffers needed to work.
  334. **/
  335.  
  336.   if (DOSBase->dl_lib.lib_Version < OS2_REVISION) {
  337.     printf("Unable to open \"dos.library\" rev. %d ...\n", OS2_REVISION);
  338.     Error();
  339.   }
  340.  
  341.   if (StarEnabled = !(DOSBase->dl_Root->rn_Flags & RNF_WILDSTAR)) {
  342.     DOSBase->dl_Root->rn_Flags |= RNF_WILDSTAR;
  343.   }
  344.  
  345.   if ((pAP = AllocMem(APlength, MEMF_CLEAR)) == NULL   ||
  346.       (Line = malloc(LINE_LENGTH)) == NULL) {
  347.     NoMem();
  348.   }
  349.   pAP->ap_Strlen    = FILENAME_MAX;
  350.   pAP->ap_BreakBits = SIGBREAKF_CTRL_C;
  351. }
  352.  
  353. char *NextFile(
  354.   char *pattern
  355. ){
  356.  
  357. /**
  358.  | Calls MatchFirst()/MatchNext() for the next file in the pattern;
  359.  | if a break has been detected NextFile() exits immediately (then
  360.  | this program will be terminated); otherwise the routine checks
  361.  | for errors, then exits.
  362. **/
  363.  
  364.   static Boolean first_entry = True;
  365.   long error;
  366.  
  367.   if (BreakFlag)  goto Clear;
  368.  
  369.   error = first_entry ? MatchFirst(pattern, pAP) : MatchNext(pAP);
  370.  
  371.   if (pAP->ap_FoundBreak) {
  372.     BreakFlag = True;
  373.     goto Clear;
  374.   }
  375.  
  376.   if (error) {
  377.     if (error == ERROR_NO_MORE_ENTRIES) {
  378.       if (first_entry) {
  379.         printf("\"%s\": no such file(s) ...\n", pattern);
  380.       }
  381.     } else {
  382.       printf("DOS error %ld returned from MatchFirst/MatchNext ...\n",
  383.               error);
  384.     }
  385.     first_entry = True;
  386.  
  387. Clear:
  388.     MatchEnd(pAP);
  389.     return NULL;
  390.   }
  391.  
  392.   first_entry = False;
  393.   return pAP->ap_Buf;
  394. }
  395.  
  396. void NoMem(void)
  397. {
  398.   puts("Couldn't obtain heap memory ...\n");
  399.   Error();
  400. }
  401.  
  402. void Syntax(void)
  403. {
  404.   printf("\n\t\t%s\n\n", VersionTag + 7);
  405.   puts("\tSyntax: grep [options] pattern [ File [ File [ ... ]]\n");
  406.   puts("Searches files (a pattern can be given in the file name(s)), for");
  407.   puts("lines containing a given pattern (with the AmigaDOS conventions).");
  408.   puts("If no file names are given, grep assumes standard input.");
  409.   puts("Exit status is 0 if any matches are found, 5 (warning) if none,");
  410.   puts("10 (error) for unreadable files or other errors.\n");
  411.   puts("Options: -c Print only the number of the matching lines;");
  412.   puts("         -f Print the name of all the scanned files;");
  413.   puts("         -i Ignore upper/lower case distinctions in the comparison;");
  414.   puts("         -l Print the name of the files with matching lines (once);");
  415.   puts("         -n Precede each line by its line number in the file;");
  416.   puts("         -v Print all lines but those that contain the pattern.\n");
  417. }
  418.